module type CONTEXT = sig
  type t

  (** A reason why a package can't be used as input to the solver. e.g. it is
      for a different platform, or conflicts with a user-provided constraint. *)
  type rejection

  val pp_rejection : rejection -> 'tag Pp.t

  (** [candidates t name] is the list of available versions of [name], in order
      of decreasing preference. If the user or environment provides additional
      constraints that mean a version should be rejected, include that here too. Rejects
      are only used for generating diagnostics reports. Candidates whose "availablity" field
      isn't satisfied must be rejected here. *)
  val candidates
    :  t
    -> OpamPackage.Name.t
    -> (OpamPackage.Version.t * (OpamFile.OPAM.t, rejection) result) list Fiber.t

  (** [user_restrictions t pkg] is the user's constraint on [pkg], if any. This is just
      used for diagnostics; you still have to filter them out yourself in [candidates]. *)
  val user_restrictions : t -> OpamPackage.Name.t -> OpamFormula.version_constraint option

  (** [filter_deps t pkg f] is used to pre-process depends and conflicts.
      [pkg] is the package which has the dependency [f].
      For example, you can use this to filter out dependencies that are only needed on Windows
      if the platform is Linux. *)
  val filter_deps : t -> OpamPackage.t -> OpamTypes.filtered_formula -> OpamTypes.formula
end

module type SOLVER = sig
  type t
  type selections
  type diagnostics

  (** [solve t package_names] finds a compatible set of package versions that
      includes all packages in [package_names] and their required dependencies. *)
  val solve : t -> OpamPackage.Name.t list -> (selections, diagnostics) result Fiber.t

  val packages_of_result : selections -> OpamPackage.t list

  (** [diagnostics d] is a message explaining why [d] failed, generated by
      performing another solve which doesn't abort on failure. *)
  val diagnostics
    :  ?verbose:bool
    -> diagnostics
    -> Stdune.User_message.Style.t Pp.t Fiber.t
end
